import { _decorator, Color, Component, EventMouse, EventTouch, Graphics, Input, input, IVec2, KeyCode, Node, NodeEventType, UITransform } from 'cc';
import { Matrix } from './game/lib/kgame/d2/math/Matrix';
import { Vec2 } from './game/lib/kgame/d2/math/Vec2';
import { BaseShape } from './game/lib/kgame/d2/shape/BaseShape';
import { BoxShape } from './game/lib/kgame/d2/shape/BoxShape';
import { CircleShape } from './game/lib/kgame/d2/shape/CircleShape';
import HollowCircleShape from './game/lib/kgame/d2/shape/HollowCircleShape';
import { PolygonShape } from './game/lib/kgame/d2/shape/PolygonShape';
import { RectShape } from './game/lib/kgame/d2/shape/RectShape';
import SectorShape from './game/lib/kgame/d2/shape/SectorShape';
import { MathUtil } from './game/lib/kgame/util/MathUtil';
const { ccclass, property } = _decorator;

let altDown = false;
let x = 0;
let y = 0;

@ccclass('Test')
export class Test extends Component {

    @property(Graphics)
    grap: Graphics;
    shapes: ShapeNode[] = [];

    start() {
        this.addShape(new CircleShape(25));
        this.addShape(new RectShape(50, 50));
        this.addShape(new BoxShape(50, 50));
        this.addShape(new PolygonShape([{ x: -10, y: 10 }, { x: 10, y: 10 }, { x: 10, y: -10 }, { x: -10, y: -10 }]));
        this.addShape(new HollowCircleShape(50, 25));
        this.addShape(new SectorShape(25, MathUtil.PI_RAD * 45));

        input.on(Input.EventType.KEY_DOWN, e => {
            if (e.keyCode === KeyCode.ALT_LEFT) {
                altDown = true;
            }
        });

        input.on(Input.EventType.KEY_UP, e => {
            if (e.keyCode === KeyCode.ALT_LEFT) {
                altDown = false;
            }
        });
    }

    update(deltaTime: number) {
        this.draw();
    }

    addShape(shape: BaseShape) {
        const node = new ShapeNode(shape);
        node.layer = this.node.layer;
        this.shapes.push(node);
        this.node.addChild(node);

        node.setPosition(x, y);
        x += 60;
        if (x >= 60 * 10) {
            x = 0;
            y -= 60;
        }
        return node;
    }


    draw() {
        this.grap.clear();
        this.shapes.forEach(shapeNode => shapeNode.update());
        this.shapes.forEach(shapeNode => this.shapes.find(target => {
            if (target === shapeNode) return;
            try {
                if (shapeNode.shape.testCollide(target.shape)) {
                    shapeNode.isCollide = true;
                    target.isCollide = true;
                }
            } catch { }
        }));
        this.shapes.forEach(shapeNode => shapeNode.draw(this.grap));
        this.shapes.forEach(shapeNode => shapeNode.drawSelf(this.grap));
    }
}

export class ShapeNode extends Node {

    tran: UITransform;
    tarnsShape = new BoxShape();
    isCollide = false;
    mat = new Matrix();

    constructor(public shape: BaseShape) {
        super();
        this.tran = this.addComponent(UITransform);
        this.tran.setContentSize(50, 50);

        this.on(NodeEventType.TOUCH_MOVE, (e: EventTouch) => {
            if (altDown) {
                const pos = new Matrix().setRotationAngle(-this.angle).transformPoint({ x: e.getUIDelta().x, y: e.getUIDelta().y })
                this.shape.offset.addPosTo(pos);
            } else {
                this.setPosition(this.position.x + e.getUIDelta().x, this.position.y + e.getUIDelta().y);
            }
        });
        this.on(NodeEventType.MOUSE_WHEEL, (e: EventMouse) => {
            if (altDown) {
                const add = e.getScrollY() * 0.01;
                this.angle -= add;
            } else {
                const add = e.getScrollY() * 0.001;
                this.setScale(this.scale.x + add, this.scale.y + add);
            }
        })
    }

    update() {
        this.isCollide = false;

        this.tarnsShape.width = this.tran.width;
        this.tarnsShape.height = this.tran.height;
        this.mat.setScaleRotationAngle(this.angle, this.scale.x, this.scale.y);
        this.tarnsShape.update(this.position, this.mat);
        this.shape.update(Vec2.T.setPos(this.position.x, this.position.y), this.mat);
    }

    draw(grap: Graphics) {
        let fill = true;
        grap.fillColor = this.isCollide ? Color.RED : Color.GREEN;
        grap.strokeColor = this.isCollide ? Color.RED : Color.GREEN;

        const { worldAABB } = this.shape;
        if (this.shape instanceof CircleShape) {
            const { worldCircle } = this.shape;
            grap.circle(worldCircle.x, worldCircle.y, worldCircle.radius);
        } else if (this.shape instanceof RectShape) {
            grap.rect(worldAABB.x, worldAABB.y, worldAABB.width, worldAABB.height);
        } else if (this.shape instanceof BoxShape) {
            const { worldPolygon } = this.shape;
            this.polygon(grap, worldPolygon);
        } else if (this.shape instanceof PolygonShape) {
            const { worldPolygon } = this.shape;
            this.polygon(grap, worldPolygon);
        } else if (this.shape instanceof HollowCircleShape) {
            const { worldCircle, worldCircleIn } = this.shape;
            grap.lineWidth = worldCircle.radius - worldCircleIn.radius;
            grap.circle(worldCircle.x, worldCircle.y, worldCircle.radius - grap.lineWidth / 2);
            grap.stroke();
            fill = false;
        } else if (this.shape instanceof SectorShape) {
            const { worldCircle, worldStartPos, worldStartRad, worldEndRad } = this.shape;
            grap.arc(worldCircle.x, worldCircle.y, worldCircle.radius, worldStartRad, worldEndRad, true);
            grap.lineTo(worldCircle.x, worldCircle.y);
            grap.lineTo(worldStartPos.x, worldStartPos.y);
        }

        fill && grap.fill();
        grap.rect(worldAABB.x, worldAABB.y, worldAABB.width, worldAABB.height);
        grap.lineWidth = 2;
        grap.strokeColor = Color.YELLOW;
        grap.stroke();
    }

    drawSelf(grap: Graphics) {
        grap.strokeColor = Color.WHITE;
        grap.lineWidth = 2;
        this.polygon(grap, this.tarnsShape.worldPolygon);
        grap.stroke();
    }

    polygon(grap: Graphics, polygon: readonly IVec2[]) {
        grap.moveTo(polygon[0].x, polygon[0].y);
        for (let i = 1; i < polygon.length; ++i) {
            grap.lineTo(polygon[i].x, polygon[i].y);
        }
        grap.close();
    }

}